package SVG;

import context.*;

import xml.XMLStreamReader;
import xml.XMLElement;

import org.apache.batik.parser.AWTPathProducer;
import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;

import java.io.Reader;
import java.io.StringReader;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.awt.Color;
import java.awt.Font;
import java.awt.EventQueue;
import java.awt.Shape;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.AffineTransform;
import java.util.Vector;
import java.util.Enumeration;

public class SVG implements Context.Constants
{ //private static final boolean DEBUG=true; // Overriding Context.Constants.DEBUG
  
  private Group mainGroup=new Group();
  public Group getMainGroup() { return(mainGroup); }
  public void paint(Graphics2D g)
  {
    if(DEBUG) Debug.println("SVG.paint: mainGroup="+mainGroup);
    
//    g.translate(-500,-500);
//    double scale=0.1;
//    g.scale(scale,scale);
    mainGroup.paint(g);
  }


  // *************************************************************************
  // *** MAIN ENTRYPOINT
  // *************************************************************************
  public static void main(String s[])
  { EventQueue.invokeLater(new Runnable()
    { public void run()
      { SVG svg=new SVG();
//        svg.TEST1();
        svg.TEST2();
//        svg.TEST3();
      } });
  }
  
  private void TEST1()
  {
//  String fname="/Documents and Settings/Administrator/My Documents/My Pictures/Hytta_Kart/TEST.svg";
    String fname="/Documents and Settings/Administrator/My Documents/Privat/Veslebu/Adkomst/Hytta_Kart/TEST.svg";
    Debug.log("SVG.main","SVG Test Leser -- STARTED: "+fname);
//    read(fname);
    read(new File(fname));    
  }
  
  private void TEST2()
  { String test=
    "<svg>"
   +"<g>"
   +"<path d=\"M9243 13198"
   +   "l 446-457 387-290-82-90-1302-1414-883 797-35 30 59 43 77 52 68 43 87 38 56 38 16 25 58 77 55 126 62 73 113 84 87 45 94 15 58 22 72 79 51 63 76 95 62 56 56 69 57 126 66 69 82 119 41 59 16 8z\""
   +   "gmwmsvg:tt=\"40/9 GWMNLKlikk for GAB eiendomsinfo\""
   +   "gmwmsvg:act=\"javascript:OnEiendomClick('2240|544|40|9||',true)\" id=\"18687912\">"
   +"</path>"
   +"</g>"
   +"</svg>";
   read(test); 
  }
  
  private void TEST3()
  {
    String d="M9243 13198"
            +"l 446-457 387-290-82-90-1302-1414-883 797-35 30 59 43 77 52 68 43 87 38 56 38 16 25 58 77 55 126 62 73 113 84 87 45" +
                    " 94 15 58 22 72 79 51 63 76 95 62 56 56 69 57 126 66 69 82 119 41 59 16 8z";
    Debug.println("START TEST3: d="+d);
    Reader reader=new StringReader(d);
    try
    {
      Shape shape=AWTPathProducer.createShape(reader,GeneralPath.WIND_NON_ZERO);
      Debug.println("RESULTING Shape="+shape);
      printExtendedGeneralPath("RESULTING Shape",(ExtendedGeneralPath)shape);
    }
    catch(Exception e) { Debug.EXCEPTION("TEST3 Failed: ",e); }
    
  }
  
  private void printExtendedGeneralPath(String title,ExtendedGeneralPath p)
  {
    Debug.println(title+"  p="+p);
    AffineTransform scale=AffineTransform.getScaleInstance(1,1);
    PathIterator i=p.getPathIterator(scale);
    double[] coords=new double[10];
    while(!i.isDone())
    { int type=i.currentSegment(coords);
      switch(type)
      {
        case PathIterator.SEG_MOVETO:
          Debug.println("MOVETO "+coords[0]+", "+coords[1]); break;
        case PathIterator.SEG_LINETO:
          Debug.println("LINETO "+coords[0]+", "+coords[1]); break;
        case PathIterator.SEG_QUADTO:
          Debug.println("QUADTO "+coords[0]+", "+coords[1]); break;
        case PathIterator.SEG_CUBICTO:
          Debug.println("CUBICTO "+coords[0]+", "+coords[1]); break;
        case PathIterator.SEG_CLOSE:
          Debug.println("CLOSE "+coords[0]+", "+coords[1]); break;
        default:
          Debug.println("UNKNOWN "+coords[0]+", "+coords[1]); break;
        
      }
      i.next();
    }
    
  }

  // *************************************************************************
  // *** CONSTRUCTOR: Main
  // *************************************************************************
  public SVG()
  {
  }

  public void read(File file)
  { FileInputStream inputStream=null;
    try
    { inputStream=new FileInputStream(file);
      read(new InputStreamReader(inputStream,"UTF-8"));
    }
    catch(Exception e) { Debug.EXCEPTION("SVG.read(File) FAILED: ",e); }
    finally { try{if(inputStream!=null) inputStream.close();}catch(Exception ignored){} }
  }

  public void read(String xmlString)
  { try { read(new StringReader(xmlString)); }
    catch(Exception e) { Debug.EXCEPTION("SVG.read(String) FAILED: ",e); }
  }

  public void read(Reader reader) throws IOException
  { XMLStreamReader xmlReader=new XMLStreamReader(reader);
      
    XMLStreamReader.Listener listener=new XMLStreamReader.Listener()
    { public void treatXML(XMLElement elt)
      {
//      if(DEBUG) Debug.println("Main.read.treatXML: "+elt);
        String tagName=elt.getName();
        if(tagName.equals("svg")) treatSVG(elt);
      }
    };
    xmlReader.parse(listener);
  }
  
  
  private void treatSVG(XMLElement elt)
  { Vector groups=elt.getChildren("g");
    for(Enumeration e=groups.elements();e.hasMoreElements();)
    {
      XMLElement grp=(XMLElement)e.nextElement();
//      treatGroup(grp);
//      mainGroup=new Group(null,grp);
      new Group(mainGroup,grp);
    }
    
  }

  // *************************************************************************
  // *** INNER INTERFACE: Element
  // *************************************************************************
  abstract class Element
  {
    protected Group parent;
    protected String id;
    protected Color stroke=null;
    protected Color fill=null;

    abstract public void paint(Graphics2D g);
    
    public Color getStroke()
    { if(stroke!=null) return(stroke);
      if(parent!=null) return(parent.getStroke());
      return(null);
    }
    
    public Color getFill()
    { if(fill!=null) return(fill);
      if(parent!=null) return(parent.getFill());
      return(null);
    }
    
  }

  // *************************************************************************
  // *** INNER CLASS: Group
  // *************************************************************************
  class Group extends Element
  {
    private Vector<Element> children=new Vector<Element>();
    
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }

    public void paint(Graphics2D g)
    {
      if(DEBUG) Debug.println("SVG.Group.paint: id="+id);
      for(Enumeration<Element> e=children.elements();e.hasMoreElements();)
      { e.nextElement().paint(g); }
      if(DEBUG) Debug.println("SVG.Group.paint: id="+id+"   DONE !");
    }

    public Group()
    { this.id="main group";
      
    }
    
    public Group(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
//      if(DEBUG) Debug.println("new Main.Group("+ident()+"): "+elt);
      if(DEBUG) Debug.println("new SVG.Group("+ident()+"): ");
      
      String stroke=elt.getAttribute("stroke");
      if(stroke!=null & !stroke.equalsIgnoreCase("none"))
      { stroke="0x"+stroke;
        if(DEBUG) Debug.println("Stroke="+stroke);
        this.stroke=Color.decode(stroke);
      }
      
      String fill=elt.getAttribute("fill");
      if(fill!=null & !fill.equalsIgnoreCase("none"))
      { fill="0x"+fill;
        if(DEBUG) Debug.println("Fill="+fill);
        this.fill=Color.decode(fill);
      }
      
//      Vector groups=elt.getChildren("g");
//      if(groups!=null)
//      { for(Enumeration e=groups.elements();e.hasMoreElements();)
//        {
//          XMLElement grp=(XMLElement)e.nextElement();
//          new Group(this,grp);
//        }
//      }
//      else
//      {
//        if(DEBUG) Debug.println("Main.Group("+ident()+"): Content="+elt);
//        
//      }
//      Vector paths=elt.getChildren("path");
//      if(paths!=null)
//      { for(Enumeration e=paths.elements();e.hasMoreElements();)
//        {
//          XMLElement path=(XMLElement)e.nextElement();
//          new Path(this,path);
//        }
//      }

      {
        Vector content=elt.getContent();
        if(content!=null)
        { for(Enumeration e=content.elements();e.hasMoreElements();)
          {
            Object sub=e.nextElement();
            if(sub instanceof XMLElement)
            {  
              XMLElement subElt=(XMLElement)sub;
              if(subElt.getName().equals("g")) new Group(this,subElt);
              else if(subElt.getName().equals("path")) new Path(this,subElt);
              else if(subElt.getName().equals("rect")) new Rect(this,subElt);
              else if(subElt.getName().equals("circle")) new Circle(this,subElt);
              else if(subElt.getName().equals("line")) new Line(this,subElt);
              else if(subElt.getName().equals("text")) new Text(this,subElt);
              else if(subElt.getName().equals("polygon")) new Polygon(this,subElt);
              else Debug.ERROR("UNKNOWN ELEMENT: "+subElt);
            }
//            else Debug.ERROR("UNKNOWN ELEMENT: "+sub);
          }
        }
      }
      
    }
    
  }
  
  // *************************************************************************
  // *** INNER CLASS: Path
  // *************************************************************************
  public class Path extends Element
  { //private Group parent;
    //private String id;
    private Shape shape;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }
    
    public Path(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Path("+ident()+"): "+elt); 
      String d=elt.getAttribute("d");
      if(DEBUG) Debug.println("START createShape: d="+d);
      Reader reader=new StringReader(d);
      try
      { shape=AWTPathProducer.createShape(reader,GeneralPath.WIND_NON_ZERO);
        if(DEBUG) Debug.println("RESULTING Shape="+shape);
        printExtendedGeneralPath("RESULTING Shape",(ExtendedGeneralPath)shape);
      }
      catch(Exception e) { Debug.EXCEPTION("createShape Failed: ",e); }
    } 
    
    public void paint(Graphics2D g)
    {
      if(DEBUG) Debug.println("SVG.Path.paint: id="+id);
//      g.draw(shape);
      
      Color fill=this.getFill();
      Color stroke=this.getStroke();
      if(fill!=null)
      { g.setColor(fill);
        g.fill(shape);
      }
      if(stroke!=null)
      { g.setColor(stroke);
        g.draw(shape);
      }
      
    }
  }
  
  // *************************************************************************
  // *** INNER CLASS: Rect 
  // *************************************************************************
  class Rect extends Element
  { //private Group parent;
    //private String id;
    private Shape shape;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }

    public void paint(Graphics2D g)
    {
      if(DEBUG) Debug.println("SVG.Rect.paint: id="+id);
//      g.draw(shape);
      
      Color fill=this.getFill();
      Color stroke=this.getStroke();
      if(fill!=null)
      { g.setColor(fill);
        g.fill(shape);
      }
      if(stroke!=null)
      { g.setColor(stroke);
        g.draw(shape);
      }
      
    }
    
    public Rect(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Rect("+ident()+"): "+elt); 
      double x=new Double(elt.getAttribute("x")).doubleValue();
      double y=new Double(elt.getAttribute("y")).doubleValue();
      double w=new Double(elt.getAttribute("width")).doubleValue();
      double h=new Double(elt.getAttribute("height")).doubleValue();
      shape=new Rectangle2D.Double(x,y,w,h);
    } 
  }
  
  // *************************************************************************
  // *** INNER CLASS: Circle 
  // *************************************************************************
  class Circle extends Element
  { //private Group parent;
    //private String id;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }
    
    public void paint(Graphics2D g) { }

    public Circle(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Circle("+ident()+"): "+elt); 
    } 
  }
  
  // *************************************************************************
  // *** INNER CLASS: Line
  // *************************************************************************
  class Line extends Element
  { //private Group parent;
    //private String id;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }

    public void paint(Graphics2D g) { }

    public Line(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Line("+ident()+"): "+elt); 
    } 
  }
  
  // *************************************************************************
  // *** INNER CLASS: Text
  // *************************************************************************
  class Text extends Element
  { //private Group parent;
    //private String id;
    int x;
    int y;
    String text;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }

    public void paint(Graphics2D g)
    {
      if(DEBUG) Debug.println("SVG.Text.paint: id="+id+", text="+text+", x="+x+", y="+y);
//      g.draw(shape);
      
      Color fill=this.getFill();
      Color stroke=this.getStroke();
//      if(fill!=null)
//      { g.setColor(fill);
//        g.fill(shape);
//      }
//      if(stroke!=null)
      {
//        g.setColor(stroke);
//        g.draw(shape);
        
        Font font=new Font("Arial",Font.BOLD,140);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawString(text,x,y);
      }
      
    }
    
    public Text(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Text("+ident()+"): "+elt); 
      x=new Integer(elt.getAttribute("x")).intValue();
      y=new Double(elt.getAttribute("y")).intValue();
      text=elt.getText();
    } 
  }
  
  // *************************************************************************
  // *** INNER CLASS: Polygon 
  // *************************************************************************
  class Polygon extends Element
  { //private Group parent;
    //private String id;
  
    public String ident()
    { if(parent==null) return(id);
      return(parent.ident()+'.'+id);
    }
    
    public void paint(Graphics2D g) { }

    public Polygon(Group parent,XMLElement elt)
    { this.parent=parent;
      if(parent!=null) parent.children.add(this);
      this.id=elt.getAttribute("id");
      if(DEBUG) Debug.println("new Main.Polygon("+ident()+"): "+elt); 
    } 
  }
  
}
